﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VCDIFF.Rebuild;
using VCDIFF.Commands;
using VCDIFF.Tools;
using System.IO;

namespace VCDIFF.Visit
{
    public class Reader : Visitor
    {
        public byte[] Data { get; set; }
        public UInt64 DataIndex = 0;
        public Header Header { get; set; }
        public IList<Window> WindowsList { get; set; }
        public string Path { get; set; }
        public UInt64 TargetWindowSize { get; set; }

        /// <summary>
        /// Reader's constructor
        /// </summary>
        /// <param name="path">Path where the Reader reads the file.</param>
        public Reader(string path)
        {
            Path = path;
            ReadFile();
        }

        public Reader()
        {

        }

        /// <summary>
        /// Visits VCDIFF File.
        /// </summary>
        /// <param name="file">VCDIFF File to visit.</param>
        public override void Visit(VCDFile file)
        {
            Header = new Header();
            Header.Read(this);

            WindowsList = new List<Window>();

            RebuildHeader rebuildHeader = new RebuildHeader();            
            if (rebuildHeader.HasHeaderData(Data))
                DataIndex = rebuildHeader.GetHeaderSize(Data);

            while ((UInt64)Data.Length > DataIndex)
            {                
                Window window = new Window();
                window.Read(this);                
            }

            RebuildInstructions rebuildInstructions = new RebuildInstructions();
            foreach (Window window in WindowsList)
                window.CommandsList = rebuildInstructions.Rebuild(window, Path);

            file.Header = Header;
            file.WindowsList = WindowsList;
        }

        public override void Visit(Header header)
        {
            RebuildHeader rebuildHeader = new RebuildHeader();
            header.SetHeader(rebuildHeader.Rebuild(Data));
        }

        public override void Visit(Window window)
        {
            RebuildWindows rebuildWindows = new RebuildWindows();
            byte[] windowData = rebuildWindows.GetWindowData(VCDIFFTools.GetRange(Data, DataIndex));
            DataIndex += (UInt64)windowData.Length;
            window.SetWindow(rebuildWindows.RebuildWindow(windowData));
            WindowsList.Add(window);
        }

        /// <summary>
        /// Visits Add command.
        /// </summary>
        /// <param name="add">Add command to visit.</param>
        public override void Visit(Add add)
        {
            TargetWindowSize += add.Size;
        }

        /// <summary>
        /// Visits Copy command.
        /// </summary>
        /// <param name="copy">Copy command to visit.</param>
        public override void Visit(Copy copy)
        {
            TargetWindowSize += copy.Size;
        }

        /// <summary>
        /// Visits Noop command.
        /// </summary>
        /// <param name="noop">Noop command to visit.</param>
        public override void Visit(Noop noop)
        {
           
        }

        /// <summary>
        /// Visits Run command.
        /// </summary>
        /// <param name="run">Run command to visit.</param>
        public override void Visit(Run run)
        {
            TargetWindowSize += run.Size;
        }

        /// <summary>
        /// Reads file specified in Reader's constructor and puts the result in Data field.
        /// </summary>
        public void ReadFile()
        {
            Data = File.ReadAllBytes(Path);
        }
    }
}
